home *** CD-ROM | disk | FTP | other *** search
/ Amiga Games Extra 1996 September / Amiga Games Extra CD-ROM 9-1996.iso / userbox / publicdomain / vim-4.2 / src / msdos.c < prev    next >
C/C++ Source or Header  |  1996-06-09  |  30KB  |  1,458 lines

  1. /* vi:set ts=4 sw=4:
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Do ":help uganda"  in Vim to read copying and usage conditions.
  6.  * Do ":help credits" in Vim to see a list of people who contributed.
  7.  */
  8.  
  9. /*
  10.  * msdos.c
  11.  *
  12.  * MSDOS system-dependent routines.
  13.  * A cheap plastic imitation of the amiga dependent code.
  14.  * A lot in this file was made by Juergen Weigert (jw).
  15.  *
  16.  * DJGPP changes by Gert van Antwerpen
  17.  */
  18.  
  19. #include <io.h>
  20. #include "vim.h"
  21. #include "globals.h"
  22. #include "option.h"
  23. #include "proto.h"
  24. #include <conio.h>
  25. #ifdef HAVE_FCNTL_H
  26. # include <fcntl.h>
  27. #endif
  28. #include <bios.h>
  29. #ifdef DJGPP
  30. # include <dpmi.h>
  31. # include <signal.h>
  32. #else
  33. # include <alloc.h>
  34. #endif
  35.  
  36. static int WaitForChar __ARGS((long));
  37. #ifdef USE_MOUSE
  38. static void show_mouse __ARGS((int));
  39. static void mouse_area __ARGS((void));
  40. #endif
  41. static int change_drive __ARGS((int));
  42. static void slash_adjust __ARGS((char_u *));
  43. static int cbrk_handler __ARGS(());
  44.  
  45. typedef struct filelist
  46. {
  47.     char_u    **file;
  48.     int        nfiles;
  49.     int        maxfiles;
  50. } FileList;
  51.  
  52. static void        addfile __ARGS((FileList *, char_u *, int));
  53. static int        pstrcmp();    /* __ARGS((char **, char **)); BCC does not like this */
  54. static void        strlowcpy __ARGS((char_u *, char_u *));
  55. static int        expandpath __ARGS((FileList *, char_u *, int, int, int));
  56.  
  57. static int cbrk_pressed = FALSE;    /* set by ctrl-break interrupt */
  58. static int ctrlc_pressed = FALSE;    /* set when ctrl-C or ctrl-break detected */
  59. static int delayed_redraw = FALSE;    /* set when ctrl-C detected */
  60.  
  61. #ifdef USE_MOUSE
  62. static int mouse_avail = FALSE;        /* mouse present */
  63. static int mouse_active;            /* mouse enabled */
  64. static int mouse_hidden;            /* mouse not shown */
  65. static int mouse_click = 0;            /* mouse status */
  66. static int mouse_x = -1;            /* mouse x coodinate */
  67. static int mouse_y = -1;            /* mouse y coodinate */
  68. static long mouse_click_time = 0;    /* biostime() of last click */
  69. static int mouse_click_count = 0;    /* count for multi-clicks */
  70. static int mouse_last_click = 0;    /* previous status at click */
  71. static int mouse_click_x = 0;        /* x of previous mouse click */
  72. static int mouse_click_y = 0;        /* y of previous mouse click */
  73. static linenr_t mouse_topline = 0;    /* topline at previous mouse click */
  74. #endif
  75.  
  76. #define BIOSTICK    55                /* biostime() increases one tick about
  77.                                         every 55 msec */
  78.  
  79.     long
  80. mch_avail_mem(special)
  81.     int special;
  82. {
  83. #ifdef DJGPP
  84.     return _go32_dpmi_remaining_virtual_memory();
  85. #else
  86.     return coreleft();
  87. #endif
  88. }
  89.  
  90. /*
  91.  * don't do anything for about 500 msec
  92.  */
  93.     void
  94. mch_delay(msec, ignoreinput)
  95.     long    msec;
  96.     int        ignoreinput;
  97. {
  98.     long    starttime;
  99.  
  100.     if (ignoreinput)
  101.     {
  102.         starttime = biostime(0, 0L);
  103.         while (biostime(0, 0L) < starttime + msec / BIOSTICK)
  104.             ;
  105.     }
  106.     else
  107.         WaitForChar(msec);
  108. }
  109.  
  110. /*
  111.  * this version of remove is not scared by a readonly (backup) file
  112.  *
  113.  * returns -1 on error, 0 otherwise (just like remove())
  114.  */
  115.     int
  116. vim_remove(name)
  117.     char_u    *name;
  118. {
  119.     (void)setperm(name, 0);    /* default permissions */
  120.     return unlink((char *)name);
  121. }
  122.  
  123. /*
  124.  * mch_write(): write the output buffer to the screen
  125.  */
  126.     void
  127. mch_write(s, len)
  128.     char_u    *s;
  129.     int        len;
  130. {
  131.     char_u    *p;
  132.     int        row, col;
  133.  
  134.     if (term_console)        /* translate ESC | sequences into bios calls */
  135.         while (len--)
  136.         {
  137.             if (p_wd)        /* testing: wait a bit for each char */
  138.                 WaitForChar(p_wd);
  139.  
  140.             if (s[0] == '\n')
  141.                 putch('\r');
  142.             else if (s[0] == ESC && len > 1 && s[1] == '|')
  143.             {
  144.                 switch (s[2])
  145.                 {
  146.                 case 'J':    clrscr();
  147.                             goto got3;
  148.  
  149.                 case 'K':    clreol();
  150.                             goto got3;
  151.  
  152.                 case 'L':    insline();
  153.                             goto got3;
  154.  
  155.                 case 'M':    delline();
  156. got3:                        s += 3;
  157.                             len -= 2;
  158.                             continue;
  159.  
  160.                 case '0':
  161.                 case '1':
  162.                 case '2':
  163.                 case '3':
  164.                 case '4':
  165.                 case '5':
  166.                 case '6':
  167.                 case '7':
  168.                 case '8':
  169.                 case '9':    p = s + 2;
  170.                             row = getdigits(&p);        /* no check for length! */
  171.                             if (p > s + len)
  172.                                 break;
  173.                             if (*p == ';')
  174.                             {
  175.                                 ++p;
  176.                                 col = getdigits(&p);    /* no check for length! */
  177.                                 if (p > s + len)
  178.                                     break;
  179.                                 if (*p == 'H' || *p == 'r')
  180.                                 {
  181.                                     if (*p == 'H')        /* set cursor position */
  182.                                         gotoxy(col, row);
  183.                                     else                /* set scroll region  */
  184.                                         window(1, row, Columns, col);
  185.                                     len -= p - s;
  186.                                     s = p + 1;
  187.                                     continue;
  188.                                 }
  189.                             }
  190.                             else if (*p == 'm')
  191.                             {
  192.                                 if (row == 0)
  193.                                     normvideo();
  194.                                 else
  195.                                     textattr(row);
  196.                                 len -= p - s;
  197.                                 s = p + 1;
  198.                                 continue;
  199.                             }
  200.                 }
  201.             }
  202.             putch(*s++);
  203.         }
  204.     else
  205.         write(1, s, (unsigned)len);
  206. }
  207.  
  208. /*
  209.  * Simulate WaitForChar() by slowly polling with bioskey(1) or kbhit().
  210.  *
  211.  * If Vim should work over the serial line after a 'ctty com1' we must use
  212.  * kbhit() and getch(). (jw)
  213.  * Usually kbhit() is not used, because then CTRL-C and CTRL-P
  214.  * will be catched by DOS (mool).
  215.  *
  216.  * return TRUE if a character is available, FALSE otherwise
  217.  */
  218.  
  219. #define FOREVER 1999999999L
  220.  
  221.     static    int
  222. WaitForChar(msec)
  223.     long    msec;
  224. {
  225.     static int    last_status = 0;
  226.     union REGS    regs;
  227.     long        starttime;
  228.     int            x, y;
  229.  
  230.     starttime = biostime(0, 0L);
  231.  
  232.     for (;;)
  233.     {
  234. #ifdef USE_MOUSE
  235.         long    clicktime;
  236.  
  237.         if (mouse_avail && mouse_active && !mouse_click)
  238.         {
  239.             regs.x.ax = 3;
  240.             int86(0x33, ®s, ®s);        /* check mouse status */
  241.                 /* only recognize button-down and button-up event */
  242.             x = regs.x.cx / 8;
  243.             y = regs.x.dx / 8;
  244.             if ((last_status == 0) != (regs.x.bx == 0))
  245.             {
  246.                 if (last_status)        /* button up */
  247.                     mouse_click = MOUSE_RELEASE;
  248.                 else                    /* button down */
  249.                 {
  250.                     mouse_click = regs.x.bx;
  251.  
  252.                     /*
  253.                      * Find out if this is a multi-click
  254.                      */
  255.                     clicktime = biostime(0, 0L);
  256.                     if (mouse_click_x == x && mouse_click_y == y &&
  257.                             mouse_topline == curwin->w_topline &&
  258.                             mouse_click_count != 4 &&
  259.                             mouse_click == mouse_last_click &&
  260.                             clicktime < mouse_click_time + p_mouset / BIOSTICK)
  261.                         ++mouse_click_count;
  262.                     else
  263.                         mouse_click_count = 1;
  264.                     mouse_click_time = clicktime;
  265.                     mouse_last_click = mouse_click;
  266.                     mouse_click_x = x;
  267.                     mouse_click_y = y;
  268.                     mouse_topline = curwin->w_topline;
  269.                     SET_NUM_MOUSE_CLICKS(mouse_click, mouse_click_count);
  270.                 }
  271.             }
  272.             else if (last_status && (x != mouse_x || y != mouse_y))
  273.                 mouse_click = MOUSE_DRAG;
  274.             last_status = regs.x.bx;
  275.             if (mouse_hidden && mouse_x >= 0 && (mouse_x != x || mouse_y != y))
  276.             {
  277.                 mouse_hidden = FALSE;
  278.                 show_mouse(TRUE);
  279.             }
  280.             mouse_x = x;
  281.             mouse_y = y;
  282.         }
  283. #endif
  284.  
  285.         if ((p_biosk ? bioskey(1) : kbhit()) || cbrk_pressed
  286. #ifdef USE_MOUSE
  287.                                                     || mouse_click
  288. #endif
  289.                 )
  290.             return TRUE;
  291.         /*
  292.          * Use biostime() to wait until our time is done.
  293.          * Don't use delay(), it doesn't work properly under Windows 95
  294.          * (because it disables interrupts?).
  295.          */
  296.         if (msec != FOREVER && biostime(0, 0L) > starttime + msec / BIOSTICK)
  297.             break;
  298.     }
  299.     return FALSE;
  300. }
  301.  
  302. /*
  303.  * mch_inchar(): low level input funcion.
  304.  * Get a characters from the keyboard.
  305.  * If time == 0 do not wait for characters.
  306.  * If time == n wait a short time for characters.
  307.  * If time == -1 wait forever for characters.
  308.  *
  309.  * return the number of characters obtained
  310.  */
  311.     int
  312. mch_inchar(buf, maxlen, time)
  313.     char_u        *buf;
  314.     int         maxlen;
  315.     long         time;
  316. {
  317.     int         len = 0;
  318.     int            c;
  319.  
  320. /*
  321.  * if we got a ctrl-C when we were busy, there will be a "^C" somewhere
  322.  * on the sceen, so we need to redisplay it.
  323.  */
  324.     if (delayed_redraw)
  325.     {
  326.         delayed_redraw = FALSE;
  327.         updateScreen(CLEAR);
  328.         setcursor();
  329.         flushbuf();
  330.     }
  331.  
  332. #ifdef USE_MOUSE
  333.     if (time != 0)
  334.         show_mouse(TRUE);
  335. #endif
  336.     if (time >= 0)
  337.     {
  338.         if (WaitForChar(time) == 0)     /* no character available */
  339.         {
  340. #ifdef USE_MOUSE
  341.             show_mouse(FALSE);
  342. #endif
  343.             return 0;
  344.         }
  345.     }
  346.     else    /* time == -1 */
  347.     {
  348.     /*
  349.      * If there is no character available within 2 seconds (default)
  350.      * write the autoscript file to disk
  351.      */
  352.         if (WaitForChar(p_ut) == 0)
  353.             updatescript(0);
  354.     }
  355.     WaitForChar(FOREVER);        /* wait for key or mouse click */
  356.  
  357. /*
  358.  * Try to read as many characters as there are.
  359.  * Works for the controlling tty only.
  360.  */
  361.     --maxlen;        /* may get two chars at once */
  362.     /*
  363.      * we will get at least one key. Get more if they are available
  364.      * After a ctrl-break we have to read a 0 (!) from the buffer.
  365.      * bioskey(1) will return 0 if no key is available and when a
  366.      * ctrl-break was typed. When ctrl-break is hit, this does not always
  367.      * implies a key hit.
  368.      */
  369.     cbrk_pressed = FALSE;
  370. #ifdef USE_MOUSE
  371.     if (mouse_click && maxlen >= 6)
  372.     {
  373.         len = 5;
  374.         *buf++ = ESC + 128;
  375.         *buf++ = 'M';
  376.         *buf++ = mouse_click;
  377.         *buf++ = mouse_x + '!';
  378.         *buf++ = mouse_y + '!';
  379.         mouse_click = 0;
  380.     }
  381.     else
  382. #endif
  383.     {
  384. #ifdef USE_MOUSE
  385.         mouse_hidden = TRUE;
  386. #endif
  387.         if (p_biosk)
  388.         {
  389.             while ((len == 0 || bioskey(1)) && len < maxlen)
  390.             {
  391.                 c = bioskey(0);            /* get the key */
  392.                 /*
  393.                  * translate a few things for inchar():
  394.                  * 0x0000 == CTRL-break            -> 3    (CTRL-C)
  395.                  * 0x0300 == CTRL-@             -> NUL
  396.                  * 0xnn00 == extended key code    -> K_NUL, nn
  397.                  * K_NUL                          -> K_NUL, 3
  398.                  */
  399.                 if (c == 0)
  400.                     c = 3;
  401.                 else if (c == 0x0300)
  402.                     c = NUL;
  403.                 else if ((c & 0xff) == 0 || c == K_NUL)
  404.                 {
  405.                     if (c == K_NUL)
  406.                         c = 3;
  407.                     else
  408.                         c >>= 8;
  409.                     *buf++ = K_NUL;
  410.                     ++len;
  411.                 }
  412.  
  413.                 *buf++ = c;
  414.                 len++;
  415.             }
  416.         }
  417.         else
  418.         {
  419.             while ((len == 0 || kbhit()) && len < maxlen)
  420.             {
  421.                 switch (c = getch())
  422.                 {
  423.                     case 0:
  424.                         /* NUL means that there is another character.
  425.                          * Get it immediately, because kbhit() doesn't always
  426.                          * return TRUE for the second character.
  427.                          */
  428.                         *buf++ = K_NUL;
  429.                         *buf++ = getch();
  430.                         len++;
  431.                         break;
  432.                     case K_NUL:
  433.                         *buf++ = K_NUL;
  434.                         *buf++ = 3;
  435.                         ++len;
  436.                         break;
  437.                     case 3:
  438.                         cbrk_pressed = TRUE;
  439.                         /*FALLTHROUGH*/
  440.                     default:
  441.                         *buf++ = c;
  442.                 }
  443.                 ++len;
  444.             }
  445.         }
  446.     }
  447. #ifdef USE_MOUSE
  448.     show_mouse(FALSE);
  449. #endif
  450.     beep_count = 0;            /* may beep again now that we got some chars */
  451.     return len;
  452. }
  453.  
  454. /*
  455.  * return non-zero if a character is available
  456.  */
  457.     int
  458. mch_char_avail()
  459. {
  460.     return WaitForChar(0L);
  461. }
  462.  
  463. /*
  464.  * We have no job control, fake it by starting a new shell.
  465.  */
  466.     void
  467. mch_suspend()
  468. {
  469.     MSG_OUTSTR("new shell started\n");
  470.     (void)call_shell(NULL, SHELL_COOKED);
  471.     need_check_timestamps = TRUE;
  472. }
  473.  
  474. extern int _fmode;
  475. /*
  476.  * we do not use windows, there is not much to do here
  477.  */
  478.     void
  479. mch_windinit()
  480. {
  481.     union REGS regs;
  482.  
  483.     _fmode = O_BINARY;        /* we do our own CR-LF translation */
  484.     flushbuf();
  485.     (void)mch_get_winsize();
  486.  
  487. #ifdef USE_MOUSE
  488. /* find out if a MS compatible mouse is available */
  489.     regs.x.ax = 0;
  490.     (void)int86(0x33, ®s, ®s);
  491.     mouse_avail = regs.x.ax;
  492. #endif
  493. }
  494.  
  495. #ifdef USE_MOUSE
  496.     static void
  497. show_mouse(on)
  498.     int        on;
  499. {
  500.     static int        was_on = FALSE;
  501.     union REGS        regs;
  502.  
  503.     if (mouse_avail)
  504.     {
  505.         if (!mouse_active || mouse_hidden)
  506.             on = FALSE;
  507.         /*
  508.          * Careful: Each switch on must be compensated by exactly one switch
  509.          * off
  510.          */
  511.         if (on && !was_on || !on && was_on)
  512.         {
  513.             was_on = on;
  514.             regs.x.ax = on ? 1 : 2;
  515.             int86(0x33, ®s, ®s);    /* show mouse */
  516.             if (on)
  517.                 mouse_area();
  518.         }
  519.     }
  520. }
  521.  
  522. /*
  523.  * Set area where mouse can be moved to.
  524.  */
  525.     static void
  526. mouse_area()
  527. {
  528.     union REGS        regs;
  529.  
  530.     if (mouse_avail)
  531.     {    
  532.         regs.x.cx = 0;    /* mouse visible between cx and dx */
  533.         regs.x.dx = ((unsigned int)Columns - 1) * 8;
  534.         regs.x.ax = 7;
  535.         (void)int86(0x33, ®s, ®s);
  536.         regs.x.cx = 0;    /* mouse visible between cx and dx */
  537.         regs.x.dx = ((unsigned int)Rows - 1) * 8;
  538.         regs.x.ax = 8;
  539.         (void)int86(0x33, ®s, ®s);
  540.     }
  541. }
  542.  
  543. #endif
  544.  
  545.     int
  546. mch_check_win(argc, argv)
  547.     int        argc;
  548.     char    **argv;
  549. {
  550.     if (isatty(1))
  551.         return OK;
  552.     return FAIL;
  553. }
  554.  
  555.     int
  556. mch_check_input()
  557. {
  558.     if (isatty(0))
  559.         return OK;
  560.     return FAIL;
  561. }
  562.  
  563. #ifdef USE_FNAME_CASE
  564. /*
  565.  * fname_case(): Set the case of the filename, if it already exists.
  566.  */
  567.     void
  568. fname_case(name)
  569.     char_u *name;
  570. {
  571.     char_u            *tail;
  572.     struct ffblk    fb;
  573.  
  574.     slash_adjust(name);
  575.     if (findfirst(name, &fb, 0) == 0)
  576.     {
  577.         tail = gettail(name);
  578.         if (STRLEN(tail) == STRLEN(fb.ff_name))
  579.             STRCPY(tail, fb.ff_name);
  580.     }
  581. }
  582. #endif
  583.  
  584. /*
  585.  * mch_settitle(): set titlebar of our window.
  586.  * Dos console has no title.
  587.  */
  588.     void
  589. mch_settitle(title, icon)
  590.     char_u *title;
  591.     char_u *icon;
  592. {
  593. }
  594.  
  595. /*
  596.  * Restore the window/icon title. (which we don't have)
  597.  */
  598.     void
  599. mch_restore_title(which)
  600.     int which;
  601. {
  602. }
  603.  
  604.     int
  605. mch_can_restore_title()
  606. {
  607.     return FALSE;
  608. }
  609.  
  610.     int
  611. mch_can_restore_icon()
  612. {
  613.     return FALSE;
  614. }
  615.  
  616. /*
  617.  * Insert user name in s[len].
  618.  */
  619.     int
  620. mch_get_user_name(s, len)
  621.     char_u    *s;
  622.     int        len;
  623. {
  624.     *s = NUL;
  625.     return FAIL;
  626. }
  627.  
  628. /*
  629.  * Insert host name is s[len].
  630.  */
  631.     void
  632. mch_get_host_name(s, len)
  633.     char_u    *s;
  634.     int        len;
  635. {
  636. #ifdef DJGPP
  637.     STRNCPY(s, "PC (32 bits Vim)", len);
  638. #else
  639.     STRNCPY(s, "PC (16 bits Vim)", len);
  640. #endif
  641. }
  642.  
  643. /*
  644.  * return process ID
  645.  */
  646.     long
  647. mch_get_pid()
  648. {
  649.     return (long)0;
  650. }
  651.  
  652. /*
  653.  * Get name of current directory into buffer 'buf' of length 'len' bytes.
  654.  * Return OK for success, FAIL for failure.
  655.  */
  656.     int
  657. mch_dirname(buf, len)
  658.     char_u    *buf;
  659.     int        len;
  660. {
  661.     return (getcwd(buf, len) != NULL ? OK : FAIL);
  662. }
  663.  
  664. /*
  665.  * Change default drive (just like _chdrive of Borland C 3.1)
  666.  */
  667.     static int
  668. change_drive(drive)
  669.     int drive;
  670. {
  671.     unsigned dummy;
  672.     union REGS regs;
  673.  
  674.     regs.h.ah = 0x0e;
  675.     regs.h.dl = drive - 1;
  676.     intdos(®s, ®s);    /* set default drive */
  677.     regs.h.ah = 0x19;
  678.     intdos(®s, ®s);    /* get default drive */
  679.     if (regs.h.al == drive - 1)
  680.         return 0;
  681.     else
  682.         return -1;
  683. }
  684.  
  685. /*
  686.  * Get absolute filename into buffer 'buf' of length 'len' bytes.
  687.  * All slashes are replaced with backslashes, to avoid trouble when comparing
  688.  * file names.
  689.  *
  690.  * return FAIL for failure, OK otherwise
  691.  */
  692.     int
  693. FullName(fname, buf, len, force)
  694.     char_u    *fname, *buf;
  695.     int        len;
  696.     int        force;
  697. {
  698.     if (fname == NULL)    /* always fail */
  699.     {
  700.         *buf = NUL;
  701.         return FAIL;
  702.     }
  703.  
  704.     if (!force && isFullName(fname))        /* allready expanded */
  705.     {
  706.         STRNCPY(buf, fname, len);
  707.         slash_adjust(buf);
  708.         return OK;
  709.     }
  710.  
  711. #ifdef __BORLANDC__        /* the old Turbo C does not have this */
  712.     if (_fullpath(buf, fname, len) == NULL)
  713.     {
  714.         STRNCPY(buf, fname, len);    /* failed, use the relative path name */
  715.         slash_adjust(buf);
  716.         return FAIL;
  717.     }
  718.     slash_adjust(buf);
  719.     return OK;
  720. #else                    /* almost the same as FullName in unix.c */
  721.     {
  722.         int        l;
  723.         char_u    olddir[MAXPATHL];
  724.         char_u    *p, *q;
  725.         int        c;
  726.         int        retval = OK;
  727.  
  728.         *buf = 0;
  729.         /*
  730.          * change to the directory for a moment,
  731.          * and then do the getwd() (and get back to where we were).
  732.          * This will get the correct path name with "../" things.
  733.          */
  734.         p = vim_strrchr(fname, '/');
  735.         q = vim_strrchr(fname, '\\');
  736.         if (q != NULL && (p == NULL || q > p))
  737.             p = q;
  738.         q = vim_strrchr(fname, ':');
  739.         if (q != NULL && (p == NULL || q > p))
  740.             p = q;
  741.         if (p != NULL)
  742.         {
  743.             if (getcwd(olddir, MAXPATHL) == NULL)
  744.             {
  745.                 p = NULL;        /* can't get current dir: don't chdir */
  746.                 retval = FAIL;
  747.             }
  748.             else
  749.             {
  750.                 q = p + 1;
  751.                 c = *q;                    /* truncate at start of fname */
  752.                 *q = NUL;
  753. #ifdef DJGPP
  754.                 STRCPY(buf, fname);
  755.                 slash_adjust(buf);        /* needed when fname starts with \ */
  756.                 if (vim_chdir(buf))        /* change to the directory */
  757. #else
  758.                 if (vim_chdir(fname))    /* change to the directory */
  759. #endif
  760.                     retval = FAIL;
  761.                 else
  762.                     fname = q;
  763.                 *q = c;
  764.             }
  765.         }
  766.         if (getcwd(buf, len) == NULL)
  767.         {
  768.             retval = FAIL;
  769.             *buf = NUL;
  770.         }
  771.         /*
  772.          * Concatenate the file name to the path.
  773.          */
  774.         l = STRLEN(buf);
  775.         if (l && buf[l - 1] != '/' && buf[l - 1] != '\\')
  776.             strcat(buf, "/");
  777.         if (p)
  778.             vim_chdir(olddir);
  779.         strcat(buf, fname);
  780.         slash_adjust(buf);
  781.         return retval;
  782.     }
  783. #endif
  784. }
  785.  
  786. /*
  787.  * Replace all slashes by backslashes.
  788.  * This used to be the other way around, but MS-DOS sometimes has problems
  789.  * with slashes (e.g. in a command name).  We can't have mixed slashes and
  790.  * backslashes, because comparing file names will not work correctly.  The
  791.  * commands that use a file name should try to avoid the need to type a
  792.  * backslash twice.
  793.  */
  794.     static void
  795. slash_adjust(p)
  796.     char_u    *p;
  797. {
  798. #ifdef DJGPP
  799.     /* DJGPP can't handle a file name that starts with a backslash, and when it
  800.      * starts with a slash there should be no backslashes */
  801.     if (*p == '\\' || *p == '/')
  802.         while (*p)
  803.         {
  804.             if (*p == '\\')
  805.                 *p = '/';
  806.             ++p;
  807.         }
  808.     else
  809. #endif
  810.     while (*p)
  811.     {
  812.         if (*p == '/')
  813.             *p = '\\';
  814.         ++p;
  815.     }
  816. }
  817.  
  818. /*
  819.  * return TRUE is fname is an absolute path name
  820.  */
  821.     int
  822. isFullName(fname)
  823.     char_u        *fname;
  824. {
  825.     return (vim_strchr(fname, ':') != NULL);
  826. }
  827.  
  828. /*
  829.  * get file permissions for 'name'
  830.  * -1 : error
  831.  * else FA_attributes defined in dos.h
  832.  */
  833.     long
  834. getperm(name)
  835.     char_u *name;
  836. {
  837.     int r;
  838.  
  839.     r = _chmod(name, 0, 0);         /* get file mode */
  840.     return r;
  841. }
  842.  
  843. /*
  844.  * set file permission for 'name' to 'perm'
  845.  *
  846.  * return FAIL for failure, OK otherwise
  847.  */
  848.     int
  849. setperm(name, perm)
  850.     char_u    *name;
  851.     long    perm;
  852. {
  853.     perm |= FA_ARCH;        /* file has changed, set archive bit */
  854.     return (_chmod((char *)name, 1, (int)perm) == -1 ? FAIL : OK);
  855. }
  856.  
  857. /*
  858.  * return TRUE if "name" is a directory
  859.  * return FALSE if "name" is not a directory
  860.  * return FALSE for error
  861.  *
  862.  * beware of a trailing backslash
  863.  */
  864.     int
  865. mch_isdir(name)
  866.     char_u *name;
  867. {
  868.     int        f;
  869.     char_u    *p;
  870.  
  871.     p = name + strlen(name);
  872.     if (p > name)
  873.         --p;
  874.     if (*p == '\\')                    /* remove trailing backslash for a moment */
  875.         *p = NUL;
  876.     else
  877.         p = NULL;
  878.     f = _chmod(name, 0, 0);
  879.     if (p != NULL)
  880.         *p = '\\';                    /* put back backslash */
  881.     if (f == -1)
  882.         return FALSE;                /* file does not exist at all */
  883.     if ((f & FA_DIREC) == 0)
  884.         return FALSE;                /* not a directory */
  885.     return TRUE;
  886. }
  887.  
  888. /*
  889.  * Careful: mch_windexit() may be called before mch_windinit()!
  890.  */
  891.     void
  892. mch_windexit(r)
  893.     int r;
  894. {
  895.     settmode(0);
  896.     stoptermcap();
  897.     flushbuf();
  898.     ml_close_all(TRUE);                /* remove all memfiles */
  899.     exit(r);
  900. }
  901.  
  902. /*
  903.  * function for ctrl-break interrupt
  904.  */
  905.     void interrupt
  906. catch_cbrk()
  907. {
  908.     cbrk_pressed = TRUE;
  909.     ctrlc_pressed = TRUE;
  910. }
  911.  
  912. /*
  913.  * ctrl-break handler for DOS. Never called when a ctrl-break is typed, because
  914.  * we catch interrupt 1b. If you type ctrl-C while Vim is waiting for a
  915.  * character this function is not called. When a ctrl-C is typed while Vim is
  916.  * busy this function may be called. By that time a ^C has been displayed on
  917.  * the screen, so we have to redisplay the screen. We can't do that here,
  918.  * because we may be called by DOS. The redraw is in mch_inchar().
  919.  */
  920.     static int
  921. cbrk_handler()
  922. {
  923.     delayed_redraw = TRUE;
  924.     return 1;                 /* resume operation after ctrl-break */
  925. }
  926.  
  927. /*
  928.  * function for critical error interrupt
  929.  * For DOS 1 and 2 return 0 (Ignore).
  930.  * For DOS 3 and later return 3 (Fail)
  931.  */
  932.     void interrupt
  933. catch_cint(bp, di, si, ds, es, dx, cx, bx, ax)
  934.     unsigned bp, di, si, ds, es, dx, cx, bx, ax;
  935. {
  936.     ax = (ax & 0xff00);        /* set AL to 0 */
  937.     if (_osmajor >= 3)
  938.         ax |= 3;            /* set AL to 3 */
  939. }
  940.  
  941. /*
  942.  * set the tty in (raw) ? "raw" : "cooked" mode
  943.  *
  944.  * Does not change the tty, as bioskey() and kbhit() work raw all the time.
  945.  */
  946.  
  947. extern void interrupt CINT_FUNC();
  948.  
  949.     void
  950. mch_settmode(raw)
  951.     int  raw;
  952. {
  953.     static int saved_cbrk;
  954. #ifndef DJGPP
  955.     static void interrupt (*old_cint)();
  956. #endif
  957.     static void interrupt (*old_cbrk)();
  958.  
  959.     if (raw)
  960.     {
  961.         saved_cbrk = getcbrk();            /* save old ctrl-break setting */
  962.         setcbrk(0);                        /* do not check for ctrl-break */
  963. #ifdef DJGPP
  964.         old_cbrk = signal(SIGINT,catch_cbrk);    /* critical error interrupt */
  965. #else
  966.         old_cint = getvect(0x24);         /* save old critical error interrupt */
  967.         setvect(0x24, catch_cint);        /* install our critical error interrupt */
  968.         old_cbrk = getvect(0x1B);         /* save old ctrl-break interrupt */
  969.         setvect(0x1B, catch_cbrk);        /* install our ctrl-break interrupt */
  970.         ctrlbrk(cbrk_handler);            /* vim's ctrl-break handler */
  971. #endif
  972.         if (term_console)
  973.             outstr(T_ME);                /* set colors */
  974.     }
  975.     else
  976.     {
  977.         setcbrk(saved_cbrk);            /* restore ctrl-break setting */
  978. #ifdef DJGPP
  979.         signal(SIGINT,old_cbrk);        /* critical error interrupt */
  980. #else
  981.         setvect(0x24, old_cint);        /* restore critical error interrupt */
  982.         setvect(0x1B, old_cbrk);        /* restore ctrl-break interrupt */
  983. #endif
  984.         /* restore ctrl-break handler, how ??? */
  985.         if (term_console)
  986.             normvideo();                /* restore screen colors */
  987.     }
  988. }
  989.  
  990. #ifdef USE_MOUSE
  991.     void
  992. mch_setmouse(on)
  993.     int        on;
  994. {
  995.     mouse_active = on;
  996.     mouse_hidden = TRUE;        /* dont show it until moved */
  997. }
  998. #endif
  999.  
  1000. /*
  1001.  * set screen mode
  1002.  * return FAIL for failure, OK otherwise
  1003.  */
  1004.     int
  1005. mch_screenmode(arg)
  1006.     char_u        *arg;
  1007. {
  1008.     int                mode;
  1009.     int                i;
  1010.     static char_u *(names[]) = {"BW40", "C40", "BW80", "C80", "MONO", "C4350"};
  1011.     static int        modes[]  = { BW40,   C40,   BW80,   C80,   MONO,   C4350};
  1012.  
  1013.     mode = -1;
  1014.     if (isdigit(*arg))                /* mode number given */
  1015.         mode = atoi((char *)arg);
  1016.     else
  1017.     {
  1018.         for (i = 0; i < sizeof(names) / sizeof(char_u *); ++i)
  1019.             if (stricmp((char *)names[i], (char *)arg) == 0)
  1020.             {
  1021.                 mode = modes[i];
  1022.                 break;
  1023.             }
  1024.     }
  1025.     if (mode == -1)
  1026.     {
  1027.         EMSG("Unsupported screen mode");
  1028.         return FAIL;
  1029.     }
  1030.     textmode(mode);                    /* use Borland function */
  1031.     return OK;
  1032. }
  1033.  
  1034. /*
  1035.  * Structure used by Turbo-C/Borland-C to store video parameters.
  1036.  */
  1037. #ifndef DJGPP
  1038. extern struct text_info _video;
  1039. #endif
  1040.  
  1041. /*
  1042.  * try to get the real window size
  1043.  * return FAIL for failure, OK otherwise
  1044.  */
  1045.     int
  1046. mch_get_winsize()
  1047. {
  1048.     int i;
  1049.     struct text_info ti;
  1050. /*
  1051.  * The screenwidth is returned by the BIOS OK.
  1052.  * The screenheight is in a location in the bios RAM, if the display is EGA or
  1053.  * VGA.
  1054.  */
  1055.     if (!term_console)
  1056.         return FAIL;
  1057.     gettextinfo(&ti);
  1058.     Columns = ti.screenwidth;
  1059.     Rows = ti.screenheight;
  1060. #ifndef DJGPP
  1061.     if (ti.currmode > 10)
  1062.         Rows = *(char far *)MK_FP(0x40, 0x84) + 1;
  1063. #endif
  1064.     /*
  1065.      * don't call set_window() when not doing full screen, since it will move
  1066.      * the cursor.  Also skip this when exiting.
  1067.      */
  1068.     if (full_screen && !exiting)
  1069.         set_window();
  1070.  
  1071.     if (Columns < MIN_COLUMNS || Rows < MIN_ROWS + 1)
  1072.     {
  1073.         /* these values are overwritten by termcap size or default */
  1074.         Columns = 80;
  1075.         Rows = 25;
  1076.         return FAIL;
  1077.     }
  1078.     check_winsize();
  1079.  
  1080.     return OK;
  1081. }
  1082.  
  1083. /*
  1084.  * Set the active window for delline/insline.
  1085.  */
  1086.     void
  1087. set_window()
  1088. {
  1089. #ifndef DJGPP
  1090.     _video.screenheight = Rows;
  1091. #endif
  1092.     window(1, 1, Columns, Rows);
  1093.     screen_start();
  1094. }
  1095.  
  1096.     void
  1097. mch_set_winsize()
  1098. {
  1099.     /* should try to set the window size to Rows and Columns */
  1100.     /* may involve switching display mode.... */
  1101.  
  1102. #ifdef USE_MOUSE
  1103.     mouse_area();            /* set area where mouse can go */
  1104. #endif
  1105. }
  1106.  
  1107. /*
  1108.  * call shell, return FAIL for failure, OK otherwise
  1109.  */
  1110.     int
  1111. call_shell(cmd, options)
  1112.     char_u    *cmd;
  1113.     int        options;        /* SHELL_FILTER if called by do_filter() */
  1114.                             /* SHELL_COOKED if term needs cooked mode */
  1115.                             /* SHELL_EXPAND if called by ExpandWildCards() */
  1116. {
  1117.     int        x;
  1118.     char_u    *newcmd;
  1119.  
  1120.     flushbuf();
  1121.  
  1122.     if (options & SHELL_COOKED)
  1123.         settmode(0);        /* set to cooked mode */
  1124.  
  1125.     if (cmd == NULL)
  1126.         x = system(p_sh);
  1127.     else
  1128.     {                 /* we use "command" to start the shell, slow but easy */
  1129.         newcmd = alloc(STRLEN(p_sh) + STRLEN(cmd) + 5);
  1130.         if (newcmd == NULL)
  1131.             x = 1;
  1132.         else
  1133.         {
  1134.             sprintf(newcmd, "%s /c %s", p_sh, cmd);
  1135.             x = system(newcmd);
  1136.             vim_free(newcmd);
  1137.         }
  1138.     }
  1139.     settmode(1);            /* set to raw mode */
  1140.  
  1141.     if (x && !expand_interactively)
  1142.     {
  1143.         msg_outchar('\n');
  1144.         msg_outnum((long)x);
  1145.         MSG_OUTSTR(" returned\n");
  1146.     }
  1147.  
  1148.     /* resettitle();                we don't have titles */
  1149.     (void)mch_get_winsize();        /* display mode may have been changed */
  1150.     return (x ? FAIL : OK);
  1151. }
  1152.  
  1153. /*
  1154.  * check for an "interrupt signal": CTRL-break or CTRL-C
  1155.  */
  1156.     void
  1157. mch_breakcheck()
  1158. {
  1159.     if (ctrlc_pressed)
  1160.     {
  1161.         ctrlc_pressed = FALSE;
  1162.         got_int = TRUE;
  1163.     }
  1164. }
  1165.  
  1166. #define FL_CHUNK 32
  1167.  
  1168.     static void
  1169. addfile(fl, f, isdir)
  1170.     FileList    *fl;
  1171.     char_u        *f;
  1172.     int            isdir;
  1173. {
  1174.     char_u        *p;
  1175.  
  1176.     if (!fl->file)
  1177.     {
  1178.         fl->file = (char_u **)alloc(sizeof(char_u *) * FL_CHUNK);
  1179.         if (!fl->file)
  1180.             return;
  1181.         fl->nfiles = 0;
  1182.         fl->maxfiles = FL_CHUNK;
  1183.     }
  1184.     if (fl->nfiles >= fl->maxfiles)
  1185.     {
  1186.         char_u    **t;
  1187.         int        i;
  1188.  
  1189.         t = (char_u **)lalloc((long_u)(sizeof(char_u *) * (fl->maxfiles + FL_CHUNK)), TRUE);
  1190.         if (!t)
  1191.             return;
  1192.         for (i = fl->nfiles - 1; i >= 0; i--)
  1193.             t[i] = fl->file[i];
  1194.         vim_free(fl->file);
  1195.         fl->file = t;
  1196.         fl->maxfiles += FL_CHUNK;
  1197.     }
  1198.     p = alloc((unsigned)(STRLEN(f) + 1 + isdir));
  1199.     if (p)
  1200.     {
  1201.         STRCPY(p, f);
  1202.         slash_adjust(p);
  1203.         /*
  1204.          * Append a backslash after directory names.
  1205.          */
  1206.         if (isdir)
  1207.             strcat(p, "\\");
  1208.     }
  1209.     fl->file[fl->nfiles++] = p;
  1210. }
  1211.  
  1212.     static int
  1213. pstrcmp(a, b)
  1214.     char_u **a, **b;
  1215. {
  1216.     return (strcmp(*a, *b));
  1217. }
  1218.  
  1219.     int
  1220. mch_has_wildcard(s)
  1221.     char_u *s;
  1222. {
  1223.     for ( ; *s; ++s)
  1224.         if (*s == '?' || *s == '*')
  1225.             return TRUE;
  1226.     return FALSE;
  1227. }
  1228.  
  1229.     static void
  1230. strlowcpy(d, s)
  1231.     char_u *d, *s;
  1232. {
  1233. #ifdef DJGPP
  1234.     if (USE_LONG_FNAME)        /* don't lower case on Windows 95/NT systems */
  1235.         while (*s)
  1236.             *d++ = *s++;
  1237.     else
  1238. #endif
  1239.         while (*s)
  1240.             *d++ = tolower(*s++);
  1241.     *d = NUL;
  1242. }
  1243.  
  1244.     static int
  1245. expandpath(fl, path, fonly, donly, notf)
  1246.     FileList    *fl;
  1247.     char_u        *path;
  1248.     int            fonly, donly, notf;
  1249. {
  1250.     char_u    *buf;
  1251.     char_u    *p, *s, *e;
  1252.     int        lastn, c, retval;
  1253.     struct ffblk fb;
  1254.  
  1255.     lastn = fl->nfiles;
  1256.     buf = alloc(STRLEN(path) + BASENAMELEN + 5);    /* make room for file name */
  1257.     if (buf == NULL)
  1258.         return 1;
  1259.  
  1260. /*
  1261.  * Find the first part in the path name that contains a wildcard.
  1262.  * Copy it into buf, including the preceding characters.
  1263.  */
  1264.     p = buf;
  1265.     s = NULL;
  1266.     e = NULL;
  1267.     while (*path)
  1268.     {
  1269.         if (*path == '\\' || *path == ':' || *path == '/')
  1270.         {
  1271.             if (e)
  1272.                 break;
  1273.             else
  1274.                 s = p;
  1275.         }
  1276.         if (*path == '*' || *path == '?')
  1277.             e = p;
  1278.         *p++ = *path++;
  1279.     }
  1280.     e = p;
  1281.     if (s)
  1282.         s++;
  1283.     else
  1284.         s = buf;
  1285.  
  1286.     /* if the file name ends in "*" and does not contain a ".", addd ".*" */
  1287.     if (e[-1] == '*' && vim_strchr(s, '.') == NULL)
  1288.     {
  1289.         *e++ = '.';
  1290.         *e++ = '*';
  1291.     }
  1292.     /* now we have one wildcard component between s and e */
  1293.     *e = NUL;
  1294.     retval = 0;
  1295.     /* If we are expanding wildcards we try both files and directories */
  1296.     if ((c = findfirst(buf, &fb, (*path || !notf) ? FA_DIREC : 0)) != 0)
  1297.     {
  1298.         /* not found */
  1299.         STRCPY(e, path);
  1300.         if (notf)
  1301.             addfile(fl, buf, FALSE);
  1302.         vim_free(buf);
  1303.         return 1; /* unexpanded or empty */
  1304.     }
  1305.     while (!c)
  1306.     {
  1307.         strlowcpy(s, fb.ff_name);    /* may expand "*" to "12345678.123" */
  1308.             /* ignore "." and ".." */
  1309.         if (*s != '.' || (s[1] != NUL && (s[1] != '.' || s[2] != NUL)))
  1310.         {
  1311.             strcat(buf, path);
  1312.             if (!mch_has_wildcard(path))
  1313.                 addfile(fl, buf, mch_isdir(buf));
  1314.             else
  1315.                 retval |= expandpath(fl, buf, fonly, donly, notf);
  1316.         }
  1317.         c = findnext(&fb);
  1318.     }
  1319.     vim_free(buf);
  1320.     qsort(fl->file + lastn, fl->nfiles - lastn, sizeof(char_u *), pstrcmp);
  1321.     return retval;
  1322. }
  1323.  
  1324. /*
  1325.  * MSDOS rebuilt of Scott Ballantynes ExpandWildCards for amiga/arp.
  1326.  * jw
  1327.  */
  1328.  
  1329.     int
  1330. ExpandWildCards(num_pat, pat, num_file, file, files_only, list_notfound)
  1331.     int     num_pat;
  1332.     char_u    **pat;
  1333.     int     *num_file;
  1334.     char_u    ***file;
  1335.     int     files_only, list_notfound;
  1336. {
  1337.     int            i, retval = 0;
  1338.     FileList    f;
  1339.  
  1340.     f.file = NULL;
  1341.     f.nfiles = 0;
  1342.  
  1343.     for (i = 0; i < num_pat; i++)
  1344.     {
  1345.         if (!mch_has_wildcard(pat[i]))
  1346.             addfile(&f, pat[i], files_only ? FALSE : mch_isdir(pat[i]));
  1347.         else
  1348.             retval |= expandpath(&f, pat[i], files_only, 0, list_notfound);
  1349.     }
  1350.  
  1351.     *num_file = f.nfiles;
  1352.     *file = (*num_file > 0) ? f.file : (char_u **)"";
  1353.  
  1354.     return (*num_file > 0) ? OK : FAIL;
  1355. }
  1356.  
  1357. #ifdef USE_VIM_CHDIR
  1358. /*
  1359.  * The normal chdir() does not change the default drive.
  1360.  * This one does.
  1361.  */
  1362.     int
  1363. vim_chdir(path)
  1364.     char *path;
  1365. {
  1366.     if (path[0] == NUL)                /* just checking... */
  1367.         return 0;
  1368.     if (path[1] == ':')                /* has a drive name */
  1369.     {
  1370.         if (change_drive(toupper(path[0]) - 'A' + 1))
  1371.             return -1;                /* invalid drive name */
  1372.         path += 2;
  1373.     }
  1374.     if (*path == NUL)                /* drive name only */
  1375.         return 0;
  1376.     return chdir(path);                /* let the normal chdir() do the rest */
  1377. }
  1378. #endif
  1379.  
  1380. #ifdef DJGPP
  1381. /*
  1382.  * djgpp_rename() works around a bug in rename (aka MoveFile) in
  1383.  * Windows 95: rename("foo.bar", "foo.bar~") will generate a
  1384.  * file whose shortfilename is "FOO.BAR" (its longfilename will
  1385.  * be correct: "foo.bar~").  Because a file can be accessed by
  1386.  * either its SFN or its LFN, "foo.bar" has effectively been
  1387.  * renamed to "foo.bar", which is not at all what was wanted.  This
  1388.  * seems to happen only when renaming files with three-character
  1389.  * extensions by appending a suffix that does not include ".".
  1390.  * Windows NT gets it right, however, with an SFN of "FOO~1.BAR".
  1391.  * This works like win95rename in win32.c, but is a bit simpler.
  1392.  *
  1393.  * Like rename(), returns 0 upon success, non-zero upon failure.
  1394.  * Should probably set errno appropriately when errors occur.
  1395.  */
  1396.  
  1397. #undef rename
  1398.  
  1399.     int
  1400. djgpp_rename(const char *OldFile, const char *NewFile)
  1401. {
  1402.     char_u    *TempFile;
  1403.     int        retval;
  1404.     int        fd;
  1405.  
  1406.     /* rename() works correctly without long file names, so use that */
  1407.     if (!_use_lfn())
  1408.         return rename(OldFile, NewFile);
  1409.  
  1410.     if ((TempFile = alloc((unsigned)(STRLEN(OldFile) + TMPNAMELEN))) == NULL)
  1411.         return -1;
  1412.     
  1413.     STRCPY(TempFile, OldFile);
  1414.     STRCPY(gettail(TempFile), TMPNAME1);
  1415.     if (rename(OldFile, TempFile))
  1416.         retval = -1;
  1417.     else
  1418.     {
  1419.         /* now create an empty file called OldFile; this prevents
  1420.          * the operating system using OldFile as an alias (SFN)
  1421.          * if we're renaming within the same directory.  For example,
  1422.          * we're editing a file called filename.asc.txt by its SFN,
  1423.          * filena~1.txt.  If we rename filena~1.txt to filena~1.txt~
  1424.          * (i.e., we're making a backup while writing it), the SFN
  1425.          * for filena~1.txt~ will be filena~1.txt, by default, which
  1426.          * will cause all sorts of problems later in buf_write.  So, we
  1427.          * create an empty file called filena~1.txt and the system will have
  1428.          * to find some other SFN for filena~1.txt~, such as filena~2.txt
  1429.          */
  1430.         if ((fd = open(OldFile, O_RDWR|O_CREAT|O_EXCL, 0444)) < 0)
  1431.             return -1;
  1432.         retval = rename(TempFile, NewFile);
  1433.         close(fd);
  1434.         vim_remove((char_u *)OldFile);
  1435.     }
  1436.     vim_free(TempFile);
  1437.  
  1438.     return retval;    /* success */
  1439. }
  1440. #endif
  1441.  
  1442. /*
  1443.  * Special version of getenv(): use $HOME when $VIM not defined.
  1444.  */
  1445.     char_u *
  1446. vim_getenv(var)
  1447.     char_u *var;
  1448. {
  1449.     char_u    *retval;
  1450.  
  1451.     retval = (char_u *)getenv((char *)var);
  1452.  
  1453.     if (retval == NULL && STRCMP(var, "VIM") == 0)
  1454.         retval = (char_u *)getenv("HOME");
  1455.  
  1456.     return retval;
  1457. }
  1458.